#include "RDPTB.h"

int Node_Construct (tree_t** new_node, double info)
{
    ASSERT (new_node);

    *new_node = (tree_t*) calloc (1, sizeof (tree_t));

    (*new_node)->info = info;
//    (*new_node)->size = 1;
    (*new_node)->type = NO_TYPE;
    (*new_node)->left = NULL;
    (*new_node)->right = NULL;
    (*new_node)->parent = NULL;

    NODE_CHECK (*new_node);

    return NODE_OK;
}

int Node_Destruct (tree_t* node)
{
    NODE_CHECK (node);

    node->info = Poison;
//    node->size = Poison;
    node->type = Poison;

    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;

    free (node);

    return NODE_OK;
}

int Node_Is_OK (tree_t* node, int* size)
{
    ASSERT (node);

    if (size == NULL)
    {
        int s = 0;
        size = &s;
    }

    (*size)++;

    if (node->left)
    {
        if (node->left->parent != node) return NODE_ERR;

        if (Node_Is_OK (node->left, size) != NODE_OK) return NODE_ERR;
    }

    if (node->right)
    {
        if (node->right->parent != node) return NODE_ERR;

        if (Node_Is_OK (node->right, size) != NODE_OK) return NODE_ERR;
    }

    return NODE_OK;
}

#define FORMAT_DUMP \
    fprintf (outstream, "\n");\
    for (i = 0; i < depth; i++)\
    {\
        fprintf (outstream, "\t");\
    }

void Node_Dump (FILE* outstream, tree_t* node, int depth)
{
    ASSERT (outstream);

//    if (depth == -1)
//    {
//        fprintf (outstream, "\t%lg <=> %c | Address: [%08x], Left: [%08x], Right: [%08x] "
//            "Parent: [%08x], Type: %d",
//            node->info, (node->type == OPERATION)? (char) node->info : 'N',
//            (int) node, (int) node->left, (int) node->right,
//            (int) node->parent, node->type);
//
//        return;
//    }

    register int i = 0;

    FORMAT_DUMP;
    fprintf (outstream, "(");

    if (node->left)
    {
        Node_Dump (outstream, node->left, depth + 1);
    }
    else
    {
        FORMAT_DUMP;
        fprintf (outstream, "\tnil");
    }

    FORMAT_DUMP;
    fprintf (outstream, "\t%lg <=> %c | Address: [%08x], Left: [%08x], Right: [%08x] "
            "Parent: [%08x], Type: %d, %s",
            node->info, (node->type == OPERATION || node->type == VARIABLE || node->type == DIFF_TYPE)? (char) node->info : 'N',
            (int) node, (int) node->left, (int) node->right,
            (int) node->parent, node->type,
            (Node_Is_OK (node, NULL) == NODE_OK)? "ok" : "ERROR!");

    if (node->right)
    {
        Node_Dump (outstream, node->right, depth + 1);
    }
    else
    {
        FORMAT_DUMP;
        fprintf (outstream, "\tnil");
    }

    FORMAT_DUMP;
    fprintf (outstream, ")");
}

#undef FORMAT_DUMP

int Node_Add_Left (root_t* tree, tree_t* node, tree_t* child)
{
    TREE_CHECK (tree);
    NODE_CHECK (node);
    NODE_CHECK (child);

//    printf ("Adding as left child [%08x] to [%08x]\n", (int) child, (int) node);

    if (node->left == NULL)
    {
        node->left = child;
        node->left->parent = node;
    }
    else printf ("AZAAZA\n");

    tree->size++;

    TREE_CHECK (tree);
    NODE_CHECK (node);

    return TREE_OK;
}

int Node_Add_Right (root_t* tree, tree_t* node, tree_t* child)
{
    TREE_CHECK (tree);
    NODE_CHECK (node);
    NODE_CHECK (child);

//    printf ("Adding as rigthful child [%08x] to [%08x]\n", (int) child, (int) node);

    if (node->right == NULL)
    {
        node->right = child;
        node->right->parent = node;
    }
    else printf ("UDSADSAAZA\n");

    tree->size++;

    TREE_CHECK (tree);
    NODE_CHECK (node);

    return TREE_OK;
}

int Node_Delete (root_t* tree, tree_t* node)
{
    TREE_CHECK (tree);
    NODE_CHECK (node);

    if (node->left) Node_Delete (tree, node->left);
    if (node->right) Node_Delete (tree, node->right);

    if (node->parent->left == node) node->parent->left = NULL;
    if (node->parent->right == node) node->parent->right = NULL;

    tree->size--;

    Node_Destruct (node);

    return TREE_OK;
}

#define PRINT_NODE \
switch (node->type)\
    {\
        case NUMBER:\
            fprintf (outstream, " %lg ", node->info);\
            break;\
        case OPERATION:\
            switch ((int) node->info)\
            {\
                case ADD:\
                fprintf (outstream, " + ");\
                break;\
                case SUB:\
                fprintf (outstream, " - ");\
                break;\
                case MUL:\
                fprintf (outstream, " * ");\
                break;\
\
                case DIV:\
                fprintf (outstream, " / ");\
                break;\
\
                default:\
                fprintf (outstream, "ERROR!!!");\
                return NODE_ERR;\
            }\
            break;\
        case VARIABLE:\
            switch ((int) node->info)\
            {\
                case X:\
                fprintf (outstream, " X ");\
                break;\
\
                default:\
                fprintf (outstream, "ERROR!!!");\
                return NODE_ERR;\
            }\
            break;\
        case DIFF_TYPE:\
            switch ((int) node->info)\
            {\
                case DIFF:\
                fprintf (outstream, " D ");\
                break;\
\
                default:\
                fprintf (outstream, "ERROR!!!");\
                return NODE_ERR;\
            }\
            break;\
        default:\
            fprintf (outstream, "ERROR!!!");\
            return NODE_ERR;\
    }

int Node_Print (FILE* outstream, tree_t* node)
{
    ASSERT (outstream);
    NODE_CHECK (node);

    fprintf (outstream, "(");

    if (node->left) Node_Print (outstream, node->left);

    PRINT_NODE;

    if (node->right) Node_Print (outstream, node->right);

    fprintf (outstream, ")");

    return NODE_OK;
}

int Node_Print_Postfix (FILE* outstream, tree_t* node)
{
    ASSERT (outstream);
    NODE_CHECK (node);

    if (node->left) Node_Print_Postfix (outstream, node->left);
    if (node->right) Node_Print_Postfix (outstream, node->right);

    PRINT_NODE;

    return NODE_OK;
}

#define DOT_PRINT_NODE \
switch (node->type)\
    {\
        case NUMBER:\
            fprintf (outstream, " \"%lg\" ", node->info);\
            break;\
        case OPERATION:\
            switch ((int) node->info)\
            {\
                case ADD:\
                fprintf (outstream, " \"+\" ");\
                break;\
                case SUB:\
                fprintf (outstream, " \"-\" ");\
                break;\
                case MUL:\
                fprintf (outstream, " \"*\" ");\
                break;\
\
                case DIV:\
                fprintf (outstream, " \"/\" ");\
                break;\
\
                default:\
                fprintf (outstream, "ERROR!!!");\
                return NODE_ERR;\
            }\
            break;\
        case VARIABLE:\
            switch ((int) node->info)\
            {\
                case X:\
                fprintf (outstream, " \"X\" ");\
                break;\
\
                default:\
                fprintf (outstream, "ERROR!!!");\
                return NODE_ERR;\
            }\
            break;\
        case DIFF_TYPE:\
            switch ((int) node->info)\
            {\
                case DIFF:\
                fprintf (outstream, " \"D\" ");\
                break;\
\
                default:\
                fprintf (outstream, "ERROR!!!");\
                return NODE_ERR;\
            }\
            break;\
        default:\
            fprintf (outstream, "ERROR!!!");\
            return NODE_ERR;\
    }

int  Node_Print_Dot (FILE* outstream, tree_t* node)
{
    ASSERT (outstream);
    NODE_CHECK (node);

    DOT_PRINT_NODE;

    if (node->left)
    {
        fprintf (outstream, " -> ");
        Node_Print_Dot (outstream, node->left);
    }
    if (node->right)
    {
        DOT_PRINT_NODE;
        fprintf (outstream, " -> ");
        Node_Print_Dot (outstream, node->right);
    }

    return NODE_OK;
}

int Node_Copy (tree_t* dest, tree_t* source)
{
    ASSERT (dest);
    ASSERT (source);

    NODE_CHECK (dest);
    NODE_CHECK (source);

    dest->info = source->info;
    dest->left = source->left;
    dest->right = source->right;
    dest->parent = source->parent;
    dest->type = source->type;

//    source->left = NULL;
//    source->right = NULL;

    if (dest->left)  dest->left->parent  = dest;
    if (dest->right) dest->right->parent = dest;

    NODE_CHECK (dest);
    NODE_CHECK (source);

    return 0;
}

tree_t* Node_Copy (tree_t* source)
{
    tree_t* dest = NULL;

    Node_Construct (&dest, 0);

    ASSERT (dest);
    ASSERT (source);

    NODE_CHECK (dest);
    NODE_CHECK (source);

    dest->info = source->info;
    dest->left = source->left;
    dest->right = source->right;
    dest->parent = source->parent;
    dest->type = source->type;

    if (dest->left)  dest->left->parent  = dest;
    if (dest->right) dest->right->parent = dest;

    fprintf (stdout, "[%08x] copied to [%08x]\n", (int) source, (int) dest);

    NODE_CHECK (dest);
    NODE_CHECK (source);
    ASSERT (dest);

    return dest;
}

int Node_Clear (tree_t* node)
{
    ASSERT (node);

    node->info = 0;
    node->type = NO_TYPE;
    node->left = NULL;
    node->right = NULL;

    return 0;
}
